﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using mqtt.client.test.Mqtt;

namespace mqtt.client.test
{
    /// <summary>
    /// https://blog.csdn.net/qq_45607873/article/details/123959719
    /// https://www.cnblogs.com/dongkuo/p/11360419.html
    /// https://blog.csdn.net/qq_49864684/article/details/116717829
    /// https://www.cnblogs.com/dongkuo/p/11360419.html
    /// https://www.cnblogs.com/dotnet-org-cn/p/13341888.html
    /// https://www.cnblogs.com/dotnet-org-cn/p/13341888.html
    /// https://zhuanlan.zhihu.com/p/80203905
    /// </summary>
    public class MyMqttClient
    {
        private readonly string host;
        private readonly int port;
        private Socket socket;

        public MyMqttClient(string host, int port)
        {
            this.host = host;
            this.port = port;

            IPAddress iP = IPAddress.Parse(host);
            IPEndPoint iPEndPoint = new IPEndPoint(iP, port);
            socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
            //创建与远程主机的连接
            socket.Connect(iPEndPoint);
        }

        public void Conect()
        {

            var client = "test";

            var clientByte = GetBytes(client);
            var len = 10 + 2 + clientByte.Length;
            List<byte> data = new();

            //固定报头
            data.Add(0x10);

            //剩余长度（Remaining Length）表示当前报文剩余部分的字节数，包括可变报头和负载的数据。剩余长度不包括用于编码剩余长度字段本身的字节数。也就是剩余长度 = 可变报头 + 有效载荷。
            //data.Add(0x13);
            foreach (var l in cateRemainLength(len))
            {
                data.Add(Convert.ToByte(l));
            }

            //可变报头
            //CONNECT 报文的可变报头按下列次序包含四个字段：协议名（Protocol Name），协议级别（Protocol Level），连接标志（Connect Flags）和保持连接（Keep Alive）。

            //协议名（Protocol Name）
            data.Add(0x00); //MSB
            data.Add(0x04);      //LSB
            data.AddRange(GetBytes("MQTT"));

            //协议级别（Protocol Level）
            data.Add(0x04);

            //连接标志（Connect Flags）
            data.Add(0x00);

            //保持连接（Keep Alive）。
            data.Add(0x01);
            data.Add(0x2C);


            //有效载荷
            //CONNECT 报文的有效载荷（payload）包含一个或多个以长度为前缀的字段，可变报头中的标志决定是否包含这些字段。如果包含的话，必须按这个顺序出现：客户端标识符，遗嘱主题，遗嘱消息，用户名，密码。
            data.Add((byte)(clientByte.Length >> 8));
            data.Add((byte)(clientByte.Length & 255));
            data.AddRange(clientByte);

            data.Dump();
            // packet_bytes = packet.to_bytes()
            var res = socket.Send(data.ToArray());


        }


        public void Public()
        {
            List<byte> data = new();

            var subject = "abc";
            var subjectByte = GetBytes(subject);

            var msg = "打发打发大幅度";
            var msgByte = GetBytes(msg);
            //固定报头
            data.Add(0x30);

            //剩余长度（Remaining Length）表示当前报文剩余部分的字节数，包括可变报头和负载的数据。剩余长度不包括用于编码剩余长度字段本身的字节数。也就是剩余长度 = 可变报头 + 有效载荷。
            var len = 2 + subjectByte.Length + msgByte.Length;
            foreach (var l in cateRemainLength(len))
            {
                data.Add(Convert.ToByte(l));
            }


            //有效载荷
            //CONNECT 报文的有效载荷（payload）包含一个或多个以长度为前缀的字段，可变报头中的标志决定是否包含这些字段。如果包含的话，必须按这个顺序出现：客户端标识符，遗嘱主题，遗嘱消息，用户名，密码。
            data.Add((byte)(subjectByte.Length >> 8));
            data.Add((byte)(subjectByte.Length & 255));
            data.AddRange(subjectByte);
            data.AddRange(msgByte);

            // packet_bytes = packet.to_bytes()
            var res = socket.Send(data.ToArray());

            var b = "";
        }


        public void Subscribe()
        {
            List<byte> data = new();

            var subject = "abc";
            var subjectByte = GetBytes(subject);


            //固定报头
            data.Add(0x82);

            //剩余长度（Remaining Length）表示当前报文剩余部分的字节数，包括可变报头和负载的数据。剩余长度不包括用于编码剩余长度字段本身的字节数。也就是剩余长度 = 可变报头 + 有效载荷。
            var len = 3 + 2 + subjectByte.Length;
            foreach (var l in cateRemainLength(len))
            {
                data.Add(Convert.ToByte(l));
            }

            data.Add(0x12);
            data.Add(0x34);

            //有效载荷
            //CONNECT 报文的有效载荷（payload）包含一个或多个以长度为前缀的字段，可变报头中的标志决定是否包含这些字段。如果包含的话，必须按这个顺序出现：客户端标识符，遗嘱主题，遗嘱消息，用户名，密码。
            data.Add((byte)(subjectByte.Length >> 8));
            data.Add((byte)(subjectByte.Length & 255));
            data.AddRange(subjectByte);
            data.Add(0x00);

            data.Dump();
            // packet_bytes = packet.to_bytes()
           // var res = socket.Send(data.ToArray());

            /*
            while (true)
            {

                int count = 0;
                int size = 512;
                byte[] buff = new byte[size];




                count = socket.Receive(buff, size, SocketFlags.None);
                count = socket.Receive(buff, size, SocketFlags.None);
                var receiveData = new List<byte>();
                while (count > 0)
                {
                    var bytes = buff.AsSpan();
                    if (receiveData.Count == 0)
                    {
                        bytes = bytes[1..count];

                        len = cateRemainLength(bytes);
                        if (len < 128)
                        {
                            bytes = bytes.Slice(1);
                        }
                        else if (len < 16384)
                        {
                            bytes = bytes.Slice(2);
                        }
                        else
                        {
                            bytes = bytes.Slice(3);
                        }
                        len = len - subjectByte.Length - 2;

                        
                        bytes = bytes.Slice(subjectByte.Length + 2);
                    }
                    else
                    {
                        bytes = bytes[..count];
                    }

                    receiveData.AddRange(bytes.ToArray());

                    if (receiveData.Count == len)
                    {
                        Console.WriteLine(GetString(receiveData.ToArray()));
                        Console.WriteLine();

                        string test = "乐山大佛你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗结尾乐山大佛你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗结尾乐山大佛你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗你好吗结尾";
                        var testByte = GetBytes(test);
                        
                        receiveData.Clear();
                    }


                    count = socket.Receive(buff, size, SocketFlags.None);
                }







                Thread.Sleep(1000);
            }
            */

           // Console.ReadLine();

        }

        private byte[] GetBytes(string str)
        {
            return System.Text.Encoding.UTF8.GetBytes(str);
        }

        private string GetString(byte[] str)
        {
            return System.Text.Encoding.UTF8.GetString(str);
        }


        private string GetString(Span<byte> str)
        {
            return System.Text.Encoding.UTF8.GetString(str);
        }

        /// <summary>
        /// 获取一个长度数据
        /// </summary>
        /// <returns></returns>
        private IEnumerable<int> cateRemainLength(int len)
        {
            do
            {
                var encodeByte = len % 128;
                len = len / 128;
                if (len > 0)
                {
                    encodeByte = encodeByte | 128;
                }

                yield return encodeByte;
            }
            while (len > 0);
        }

        /// <summary>
        /// 获取一个长度数据
        /// </summary>
        /// <returns></returns>
        private int cateRemainLength(Span<byte> bytes)
        {
            var i = 0;
            var value = 0;
            var multiplier = 1;

            int encodedByte;
            do
            {
                value += (bytes[i] & 127) * multiplier;

                if (multiplier > 128 * 128 * 128)
                    throw new Exception("字段太长，超出限制了");


                multiplier *= 128;

                encodedByte = bytes[i];

                i++;

            }
            while ((encodedByte & 128) != 0);


            return value;
        }
    }
}
